#import "EAGLView.h"
#import <QuartzCore/QuartzCore.h>

EAGLContext* g_context = nil;
GLuint g_frame_buffer = 0;
GLuint g_render_buffer = 0;

extern "C" void UpdateTouchBegan(unsigned char touchId, int x, int y);
extern "C" void UpdateTouchEnded(unsigned char touchId, int x, int y);
extern "C" void UpdateTouchMoved(unsigned char touchId, int x, int y);

//set context
extern "C" void EAGLViewBeginRender()
{
	// Make sure that you are drawing to the current context
	[EAGLContext setCurrentContext:g_context];	
	glBindFramebufferOES(GL_FRAMEBUFFER_OES, g_frame_buffer);
}

//swap buffer
extern "C" void EAGLViewEndRender()
{
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, g_render_buffer);
	[g_context presentRenderbuffer:GL_RENDERBUFFER_OES];	
}


@implementation EAGLView

// You must implement this
+ (Class) layerClass
{
	return [CAEAGLLayer class];
}

- (void) layoutSubviews
{
	[EAGLContext setCurrentContext:g_context];
	[self DestroyFramebuffer];
	[self CreateFramebuffer];
}
- (void) ClearTouch
{
	for (int i = 0; i < MAX_TOUCHES - 1; i++)
	{
		_current_touches[i] = nil;
	}
	_num_touches = 0;
}

- (id) initWithFrame:(CGRect)frame
{
	 self = [super initWithFrame:frame];

	// convert base layer
	CAEAGLLayer *eagl_layer = (CAEAGLLayer*)self.layer;
	
	eagl_layer.opaque = YES;//NEW
	
	eagl_layer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; //NEW
	
	// create opengl context
	g_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
		
	if(!g_context || ![EAGLContext setCurrentContext:g_context] || ![self CreateFramebuffer]) 
	{
		[self release];
		return nil;
	}
	
	//g_context = _context;
	
	for (int i = 0; i < MAX_TOUCHES - 1; i++)
	{
		_current_touches[i] = nil;
	}
	_num_touches = 0;
	
	_touch_id_gen = 0;
	
	return self;
}

- (BOOL) CreateFramebuffer
{
	_frame_buffer = 0;
	_render_buffer = 0;
	_depth_buffer = 0;
	
	//back up old
	GLuint old_render_buffer;
	GLuint old_frame_buffer;
	glGetIntegerv(GL_RENDERBUFFER_BINDING_OES, (GLint *) &old_render_buffer);
	glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, (GLint *) &old_frame_buffer);
	
	//create framebuffer
	glGenFramebuffersOES(1, &_frame_buffer);
	//bind to framebuffer
	glBindFramebufferOES(GL_FRAMEBUFFER_OES, _frame_buffer);
	
	// create render buffer
	glGenRenderbuffersOES(1, &_render_buffer);
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, _render_buffer);
	[g_context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self.layer];
	//bind to renderbuffer
	glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, _render_buffer);
	
	glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &_backbuffer_width);
	glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &_backbuffer_height);
	
#ifdef USE_DEPTH_BUFFER
	//create depth buffer
	glGenRenderbuffersOES(1, &_depth_buffer);
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, _depth_buffer);
	glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT24_OES, _backbuffer_width, _backbuffer_height);
	//bind to depth buffer
	glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, _depth_buffer);
	glBindFramebufferOES(GL_FRAMEBUFFER_OES, _frame_buffer);
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, _render_buffer);
#endif
	
	if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) 
	{
		NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
		//restore old
		glBindFramebufferOES(GL_FRAMEBUFFER_OES, old_frame_buffer);
		glBindRenderbufferOES(GL_RENDERBUFFER_OES, old_render_buffer);
		
		return NO;
	}
	
	g_frame_buffer = _frame_buffer;
	g_render_buffer = _render_buffer;
	
	//::glShadeModel(GL_SMOOTH);			
	//::glClearColor(0.0f, 0.0f, 0.0f, 0.0f);	
	//::glClearDepthf(1.0f);			
	//::glEnable(GL_DEPTH_TEST);				
	//::glDepthFunc(GL_LEQUAL);				
	//::glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	//set viewport
	::glViewport(0, 0, _backbuffer_width, _backbuffer_height);
	//config opengl
	//enable smooth shading
	//::glShadeModel(GL_SMOOTH);
	//set clear color value
	::glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	//set clear depth buffer value
	::glClearDepthf(1.0f);
	//enable z-buff
	::glDepthMask(GL_FALSE);	
	::glDisable(GL_DEPTH_TEST);
	//::glDepthMask(GL_TRUE);	
	//::glEnable(GL_DEPTH_TEST);
	//::glDepthFunc(GL_LEQUAL);
	//alpha test
	::glDisable(GL_ALPHA_TEST);
	//::glEnable(GL_ALPHA_TEST);
	//::glAlphaFunc(GL_GREATER, 0.0f);
	::glDisable(GL_CULL_FACE);
	//::glEnable(GL_CULL_FACE);
	//::glCullFace(GL_BACK);
	//::glFrontFace(GL_CCW);
	//::glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	//::glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
	//::glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
	//::glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
	::glMatrixMode(GL_PROJECTION);						
	::glLoadIdentity();
	
	::glOrthof(0, _backbuffer_width, _backbuffer_height, 0, -1.0f, 1.0f);

	return YES;
}


- (void) DestroyFramebuffer
{
	glDeleteFramebuffersOES(1, &_frame_buffer);
	_frame_buffer = 0;
	glDeleteRenderbuffersOES(1, &_render_buffer);
	_render_buffer = 0;
	
	if (_depth_buffer) 
	{
		glDeleteRenderbuffersOES(1, &_depth_buffer);
		_depth_buffer = 0;
	}
}


//multi touches process

- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
	[self UpdateTouches:event];
}

- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
	[self UpdateTouches:event];
}

- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
	
	//check end multi touches
	if ([touches count] == [[event touchesForView:self] count]) 
	{
		// last finger has lifted....
	}
	
	[self UpdateTouches:event];
}

- (void) UpdateTouches:(UIEvent*)event
{
    NSSet *touches = [event allTouches];

    for (UITouch *atouch in touches)
    {
    	//check to see whether this touch event already exists
		int index = [self GetTouchIndex: atouch];
    	if (index == -1 && atouch.phase == UITouchPhaseBegan) //doesn't exist yet, add it
    	{
    		index = _num_touches;
			[self AddTouch:atouch];
    	}

    	//get current touch coordinates
		CGPoint touch_point = [atouch locationInView:self];

		if (index != -1)
		{
			if (atouch.phase == UITouchPhaseBegan)
			{
				UpdateTouchBegan(_touches_id[index], touch_point.x, touch_point.y);
			}
			else if (atouch.phase == UITouchPhaseMoved)
			{
				UpdateTouchMoved(_touches_id[index], touch_point.x, touch_point.y);
			}
			else if (atouch.phase == UITouchPhaseEnded || atouch.phase == UITouchPhaseCancelled)
			{
				UpdateTouchEnded(_touches_id[index], touch_point.x, touch_point.y);
				[self RemoveTouch:atouch];
			}
	   }
    }
}

- (int) GetTouchIndex: (UITouch*) touch
{
	int i = 0;
	for (i = 0 ; i < _num_touches ; i++)
	{
		if (touch == _current_touches[i])
		{
			return i;
		}
	}
	i = -1;
	
	return i;
}

- (void) AddTouch: (UITouch*) touch
{
	if (_num_touches < MAX_TOUCHES)
	{
		_current_touches[_num_touches] = touch;
		_touches_id[_num_touches] = _touch_id_gen++;
		_num_touches++;
	}
}

- (void) RemoveTouch: (UITouch*) touch
{
	int index = [self GetTouchIndex:touch];
	
	if (index != -1)
	{
		for (int i = index; i < MAX_TOUCHES - 1; i++)
		{
			_current_touches[i] = _current_touches[i+1];
			_touches_id[i] = _touches_id[i+1];
		}

		_num_touches--;
	}
}



- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{

#ifdef USE_AUTO_ROTATE_VIEW
	return YES; 
#else
	return NO; 
#endif

}

- (void) dealloc
{
	[self DestroyFramebuffer];
	
	if ([EAGLContext currentContext] == g_context)
	{
		[EAGLContext setCurrentContext:nil];
	}
	
	[g_context release];
	g_context = nil;
	
	[super dealloc];
}

@end
